use rust_decimal::Decimal;
use std::collections::HashMap;
use crate::{Result, Error};

pub use crate::lexer::Token;

pub trait Parser: Sized {
    fn try_parse(stream: &mut TokenStream, offset: u32) -> Result<Self> {
        let checkpoint = stream.checkpoint();
        match Self::parse(stream, offset) {
            Ok(result) => {
                stream.commit();
                Ok(result)
            },
            err => {
                stream.rollback(checkpoint);
                err
            }
        }
    }
    fn parse(stream: &mut TokenStream, offset: u32) -> Result<Self>;
}

#[derive(Debug)]
pub struct TokenStream { 
    tokens: Vec<(Token, u32, u32)>,
    redefs: HashMap<String, u32>,
    cursor: usize,
}

impl TokenStream {
    pub fn new(tokens: Vec<(Token, u32, u32)>) -> TokenStream {
        TokenStream{tokens, cursor: 0, redefs: HashMap::new()}
    }

    pub fn peek(&self) -> &Token{
        &self.tokens[self.cursor].0
    } 
    pub fn column(&self) -> u32 {
        self.tokens[self.cursor].1
    }
    pub fn row(&self) -> u32{
        self.tokens[self.cursor].2
    }
    
    pub fn fwd(&mut self){
        self.cursor += 1;
    }
    pub fn next(&mut self) -> Token {
        self.cursor += 1;
        self.tokens[self.cursor-1].0.clone()
    }
    pub fn must_be(&mut self, t: Token) -> Result<()> {
        if self.peek() == &t {
            self.fwd();
            Ok(())
        } else {
            Err(self.parse_error(t))
        }
    }
    pub fn may_be(&mut self, t: Token) -> bool{
        if self.peek() == &t {
            self.fwd();
            true
        } else {
            false
        }
    }
    pub fn parse_decimal(&mut self) -> Result<Decimal>{
        if let Token::Decimal(n) = self.peek().clone(){
            self.fwd();
            Ok(n)
        } else {
            Err(self.parse_error("decimal"))
        }
    }

    pub fn parse_unsigned(&mut self) -> Result<u32>{
        if let Token::Unsigned(n) = self.peek().clone(){
            self.fwd();
            Ok(n)
        } else {
            Err(self.parse_error("unsigned"))
        }
    }

    pub fn parse_string(&mut self) -> Result<String>{
        if let Token::String(s) = self.peek().clone(){
            self.fwd();
            Ok(s)
        } else {
            Err(self.parse_error("string"))
        }
    }

    pub fn parse_ident(&mut self) -> Result<String> {
        if let Token::Ident(s) = self.peek().clone(){
            self.fwd();
            Ok(s)
        } else {
            Err(self.parse_error("ident"))
        }
    }

    pub fn parse_error<T: std::fmt::Display>(&self, what: T) -> Error {
        let msg = format!("{}", what);
        Error::ParseError{ msg, col: self.column(), row: self.row() }
    }

    pub fn save_redefine(&mut self, name: String, offset: u32) {
        self.redefs.insert(name, offset);
    }

    pub fn get_redefine(&self, name: String) -> Option<u32> {
        Some(*self.redefs.get(&name)?)
    }
    
    pub fn checkpoint(&mut self) -> Checkpoint {
        Checkpoint { cursor: self.cursor }
    }

    pub fn commit(&mut self) {
    }

    pub fn rollback(&mut self, checkpoint: Checkpoint){
        self.cursor = checkpoint.cursor;
    }
}

pub struct Checkpoint {
    pub cursor: usize,
}


